//gcc -lpthread -o p1 practica1.c
//./p1 7 2 1 1 4 4 
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>

typedef struct {
	//Longitud: Tamaño de la cola
	//Cantidad: Numero de botellas en cola; longitud =-1 -> proceso anterior finalizado
	int longitud, cantidad;

} Cola;

typedef struct {
	Cola cola1, cola2;
	int anyadir, llenar, tapar;
	int estado;
	int ts;
	time_t start;
	int nBot;

} Param;

pthread_mutex_t mux_cola1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mux_cola2 = PTHREAD_MUTEX_INITIALIZER;
Param param;

void printParameters() {
	printf("\nVelocidad Añadir Botellas: %d", param.anyadir);
	printf("\nVelocidad Llenar Botellas: %d", param.llenar);
	printf("\nVelocidad Tapar Botellas: %d", param.tapar);
	printf("\nLongitud Cola1: %d", param.cola1.longitud);
	printf("\nLongitud Cola2: %d\n", param.cola2.longitud);
	printf("\nBotellas embotelladas: %d\n", param.nBot);	
}

int isTimeValid(time_t start) {
	time_t end = time(NULL);
	return difftime(end, start) < param.ts;
}

int getCurrentSecond(time_t start, time_t end) {
	return difftime(end, start);
}

void trace(char *maquina, char *accion){
	int end = getCurrentSecond(param.start,time(NULL));
	printf("| %d\t | %s\t | %s\t | %d\t | %d\t |\n", end, maquina, accion, param.cola1.cantidad, param.cola2.cantidad);

}

void Cabecera (){
	printf("------------------------------------------------------------------");
	printf("\n| TS \t | MÁQUINA \t | ACCIÓN \t\t | COLA1 | COLA2 |\n");
	printf("------------------------------------------------------------------\n");
}

void initParameters(Param *param, char *argv[]) {
	(*param).ts = atoi(argv[1]);
	(*param).anyadir = atoi(argv[2]);
	(*param).llenar = atoi(argv[3]);
	(*param).tapar = atoi(argv[4]);
	(*param).estado = -1;
	(*param).cola1.cantidad = 0;
	(*param).cola2.cantidad = 0;
	(*param).cola1.longitud = atoi(argv[5]);
	(*param).cola2.longitud = atoi(argv[6]);
	(*param).nBot=0;
}

void *maquina1(void * arg) {

	while (1) {

		if ((param.ts > 0) && (param.cola1.longitud != param.cola1.cantidad)) {
			trace("Añadir", "Añadiendo Botella");
			sleep(param.anyadir);
			if (pthread_mutex_lock(&mux_cola1) == 0) {
				param.cola1.cantidad = param.cola1.cantidad + 1;
				trace("Añadir", "Botella Añadida");
				if (pthread_mutex_unlock(&mux_cola1) != 0) {
					trace("Añadir", "Error Desbloquer Cola1");
				}
			} else
				trace("Añadir", "Error Bloquer Cola1");
		}
		if (param.ts == 0) {
			trace("Añadir", "Tiempo Acabado");
			trace("Añadir", "Apagando Maquina");
			param.estado = 1;
			pthread_exit(NULL);
		}
	}
}

void *maquina2(void * arg) {

	while (1) {
		if ((param.cola1.cantidad > 0) && (param.cola2.longitud != param.cola2.cantidad)) {
			if (pthread_mutex_lock(&mux_cola1) == 0) {
				param.cola1.cantidad = param.cola1.cantidad - 1;
				trace("Rellenar", "Sacando Botella");
			}
			if (pthread_mutex_unlock(&mux_cola1) != 0) {
				trace("Rellenar", "Error Desbloquer Cola1");
			}
			trace("Rellenar", "Rellenando Botella");
			sleep(param.llenar);
			if (pthread_mutex_lock(&mux_cola2) == 0) {
				param.cola2.cantidad = param.cola2.cantidad + 1;
				trace("Rellenar", "Poniendo Botella");
			}
			if (pthread_mutex_unlock(&mux_cola2) != 0) {
				trace("Rellenar", "Error Desbloquer Cola2");
			}
		} else {
			//trace("RELLENAR", "Sin Botellas");
		}

		if ((param.estado == 1) && (param.cola1.cantidad == 0)) {
			trace("Rellenar", "Apagando Maquina");
			param.estado = 2;
			pthread_exit(NULL);
		}
	}
}

void *maquina3(void * arg) {
	while (1) {
		if (param.cola2.cantidad > 0) {
			trace("Tapar  ", "Tapando Botella");
			sleep(param.tapar);
			if (pthread_mutex_lock(&mux_cola2) == 0) {
				param.cola2.cantidad -= 1;
				if (pthread_mutex_unlock(&mux_cola2) != 0) {
					trace("Tapar  ", "Error Desbloquer Cola2");
				}
			}
			trace("Tapar  ", "Botella Tapada");
			param.nBot+=1;
		} else {
			//trace("TAPAR(M3)", "Sin Botellas");
		}
		if ((param.estado == 2) && (param.cola2.cantidad == 0)) {
			trace("Tapar  ", "Apagando Maquina");
			pthread_exit(NULL);
		}
	}
}

int main(int argc, char *argv[]) {

	time_t start, end;
	
	initParameters(&param, argv);
	printf("\nTiempo Simulación: %d", param.ts);
	printParameters(param);

	pthread_t threadM1;
	pthread_t threadM2;
	pthread_t threadM3;

	param.start = time(NULL);

	Cabecera();
	pthread_create(&threadM1, NULL, maquina1, NULL);
	pthread_create(&threadM2, NULL, maquina2, NULL);
	pthread_create(&threadM3, NULL, maquina3, NULL);

	while (isTimeValid(param.start)) {
	}

	param.ts = 0;

	(void) pthread_join(threadM1, NULL);
	(void) pthread_join(threadM2, NULL);
	pthread_mutex_destroy(&mux_cola1);
	(void) pthread_join(threadM3, NULL);
	pthread_mutex_destroy(&mux_cola2);

	printf("\nFinal, %d botellas embotelladas\n", param.nBot);

}
